package com.dgsrz.demo.activity;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.PointF;
import android.opengl.GLSurfaceView;
import android.opengl.GLUtils;
import android.util.DisplayMetrics;
import android.view.Window;
import com.dgsrz.demo.R;
import com.dgsrz.demo.game.Slider;
import com.dgsrz.demo.primitive.Rectangle;
import com.dgsrz.demo.texture.GradientTexture;
import com.dgsrz.demo.util.Constants;
import com.dgsrz.demo.util.FPSLogger;
import com.dgsrz.demo.util.GLESUtils;
import com.dgsrz.demo.util.Spline;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.opengles.GL11ExtensionPack;
import java.util.ArrayList;
import java.util.LinkedList;

/**
 * Created by dgsrz on 15/7/2.
 */
public class DemoGameScene {

    private static final int TEXTURE_WIDTH = 128;
    private static final int TEXTURE_HEIGHT = 1;
    private static final int PLAYABLE_WIDTH = 640;
    private static final int PLAYABLE_HEIGHT = 480;

    private Context mContext;

    private GradientTexture mGradientTexture;
    private Slider mSlider;

    private FPSLogger mLogger = FPSLogger.INSTANCE;
    private RenderMode mRenderMode = RenderMode.SLIDER;

    public DemoGameScene(Context context) {
        this.mContext = context;
    }

    /**
     * Settings this will change the behaviour of this demo.
     *
     * @param mode Expected mode
     */
    public void setRenderMode(RenderMode mode) {
        this.mRenderMode = mode;
    }

    public enum RenderMode {
        TEXTURE, SLIDER
    }

    public final Slider getSlider() {
        return mSlider;
    }

    public class Renderer implements GLSurfaceView.Renderer {

        private boolean mContextSupportsFrameBufferObject;

        private int mSurfaceWidth;
        private int mSurfaceHeight;

        private int mRectangleTexture;
        private int mRectangleTarget;

        private int mSliderTexture;
        private int mSliderTarget;

        private int mBackdropTexture;

        private Rectangle mOnScreenLayer;

        @Override
        public void onDrawFrame(GL10 gl) {
            GLESUtils.checkGLErrors(gl);
            if (mContextSupportsFrameBufferObject) {
                GL11ExtensionPack gl11ep = (GL11ExtensionPack) gl;

                if (mRenderMode == RenderMode.TEXTURE) {
                    drawTexture(gl, mSurfaceWidth, mSurfaceHeight);

                } else if (mRenderMode == RenderMode.SLIDER) {
                    gl11ep.glBindFramebufferOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES, mRectangleTarget);
                    drawTexture(gl, TEXTURE_WIDTH, TEXTURE_HEIGHT);

                    gl11ep.glBindFramebufferOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES, mSliderTarget);
                    drawOffScreen(gl, PLAYABLE_WIDTH, PLAYABLE_HEIGHT);

                    gl11ep.glBindFramebufferOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES, 0);
                    drawOnScreen(gl, mSurfaceWidth, mSurfaceHeight);
                }
            } else {
                gl.glClearColor(0, 0.5f, 1f, 0);
                gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
            }
            mLogger.finishGLDraw();
        }

        @Override
        public void onSurfaceChanged(GL10 gl, int width, int height) {
            GLESUtils.checkGLErrors(gl);
            mSurfaceWidth = width;
            mSurfaceHeight = height;
            gl.glViewport(0, 0, width, height);
        }

        @Override
        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
            mContextSupportsFrameBufferObject = GLESUtils.checkExtensionCompatibility(gl, GLESUtils.GL_EXT_FBO);
            if (mContextSupportsFrameBufferObject) {
                mRectangleTexture = GLESUtils.createTargetTexture(gl, TEXTURE_WIDTH, TEXTURE_HEIGHT);
                mRectangleTarget = GLESUtils.createFrameBuffer(gl, TEXTURE_WIDTH, TEXTURE_HEIGHT, mRectangleTexture);

                mSliderTexture = GLESUtils.createTargetTexture(gl, PLAYABLE_WIDTH, PLAYABLE_HEIGHT);
                mSliderTarget = GLESUtils.createFrameBuffer(gl, PLAYABLE_WIDTH, PLAYABLE_HEIGHT, mSliderTexture);

                mBackdropTexture = loadGLTexture(gl, mContext);

                mGradientTexture = new GradientTexture(gl);

                mSlider = new Slider(gl);
//                mSlider.setCurveType(Spline.CurveTypes.Catmull);
//                mSlider.setControlPoints(new ArrayList<PointF>(4) {
//                    {
//                        add(new PointF(96, 240));
//                        add(new PointF(240, 96));
//                        add(new PointF(400, 96));
//                        add(new PointF(544, 240));
//                    }
//                });
//                mSlider.setCurveType(Spline.CurveTypes.Bezier);
                mSlider.setControlPoints(ConventToPoints("48,340,743,6,0,B|15:298|-15:255|-27:144|39:78|88:52|178:48|178:48|213:59|281:77|346:94|449:61|511:30|511:30|479:84|468:103|408:134|323:145|323:145|364:169|407:177|440:182|440:182|404:203|348:213|288:201|288:201|307:222|340:243|374:255|374:255|328:265|284:261|255:254|255:254|270:286|292:302|292:302|239:309|189:307|142:298|99:282|87:243|87:243|58:169|120:78|237:90|272:149|225:231|179:234|164:212|164:212,1,2096.79800908597"));
                mSlider.getPathPoints();

                mOnScreenLayer = new Rectangle(gl, PLAYABLE_WIDTH, PLAYABLE_HEIGHT);
            }
        }

        private void drawOnScreen(GL10 gl, int width, int height) {
            gl.glViewport(0, 0, width, height);
            gl.glMatrixMode(GL10.GL_PROJECTION);
            gl.glLoadIdentity();
            gl.glOrthof(0, width, height, 0, 0, 65536.0f);

            gl.glClearColor(0, 0.5f, 1f, 0);
            gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

            gl.glMatrixMode(GL10.GL_MODELVIEW);
            gl.glLoadIdentity();

            gl.glEnable(GL10.GL_TEXTURE_2D);

            gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
            gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

            gl.glDisable(GL10.GL_DEPTH_TEST);
            gl.glEnable(GL10.GL_BLEND);
            gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);

            gl.glBindTexture(GL10.GL_TEXTURE_2D, mBackdropTexture);
            mOnScreenLayer.drawPrimitives();

            gl.glBindTexture(GL10.GL_TEXTURE_2D, mSliderTexture);
            gl.glColor4f(1, 1, 1, mSlider.getAlpha());
            mOnScreenLayer.drawPrimitives();
            gl.glColor4f(1, 1, 1, 1f);

            gl.glDisable(GL10.GL_BLEND);

            gl.glBindTexture(GL10.GL_TEXTURE_2D, 0);
            gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
            gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
        }

        private void drawOffScreen(GL10 gl, int width, int height) {
            gl.glViewport(0, 0, width, height);
            gl.glMatrixMode(GL10.GL_PROJECTION);
            gl.glLoadIdentity();
            gl.glOrthof(0, width, height, 0, 0, 65536.0f);

            gl.glClearColor(0, 0, 0, 0);
            gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
            gl.glBindTexture(GL10.GL_TEXTURE_2D, mRectangleTexture);

            gl.glMatrixMode(GL10.GL_MODELVIEW);
            gl.glLoadIdentity();

            gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
            gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

            gl.glActiveTexture(GL10.GL_TEXTURE0);

            gl.glDisable(GL10.GL_BLEND);
            gl.glEnable(GL10.GL_DEPTH_TEST);
            gl.glDepthFunc(GL10.GL_LEQUAL);

            mSlider.drawObject();

            gl.glDisable(GL10.GL_DEPTH_TEST);

            gl.glBindTexture(GL10.GL_TEXTURE_2D, 0);
            gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
            gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
        }

        private void drawTexture(GL10 gl, int width, int height) {
            gl.glViewport(0, 0, width, height);
            gl.glMatrixMode(GL10.GL_PROJECTION);
            gl.glLoadIdentity();
            gl.glOrthof(0.0f, 1.0f, 1.0f, 0.0f, -1.0f, 1.0f);

            gl.glClearColor(0, 0, 0, 0);
            gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
            gl.glMatrixMode(GL10.GL_MODELVIEW);
            gl.glLoadIdentity();

            gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
            gl.glEnableClientState(GL10.GL_COLOR_ARRAY);

            gl.glDisable(GL10.GL_BLEND);
            gl.glDisable(GL10.GL_DEPTH_TEST);

            mGradientTexture.drawTexture();

            gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
            gl.glDisableClientState(GL10.GL_COLOR_ARRAY);
        }

        private int loadGLTexture(GL10 gl, Context context) {
            int[] textures = new int[1];

            Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(),
                    R.raw.backdrop);

            gl.glGenTextures(1, textures, 0);
            gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
            gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
            gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);

            GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);

            bitmap.recycle();
            return textures[0];
        }

        private LinkedList<ArrayList<PointF>> ConventToPoints(String line) {
            int index = 0;
            LinkedList<ArrayList<PointF>> points = new LinkedList<>();
            points.add(new ArrayList<PointF>());
            String[] data = line.split("[,]");
            points.get(index).add(ConventToRealCorrd(new PointF(Float.valueOf(data[0]), Float.valueOf(data[1]))));
            for (String temp : data[5].split("[|]")) {
                if (temp.contains(":")) {
                    data = temp.split("[:]");
                    PointF point = ConventToRealCorrd(new PointF(Float.valueOf(data[0]), Float.valueOf(data[1])));
                    PointF lastPoint = points.get(index).get(points.get(index).size() - 1);
                    if (point.x == lastPoint.x && point.y == lastPoint.y) {
                        points.add(new ArrayList<PointF>());
                        index++;
                    }
                    points.get(index).add(point);
                } else {
                    mSlider.setCurveType(Spline.getCurveType(temp.charAt(0)));
                }
            }
            return points;
        }

        public PointF ConventToRealCorrd(PointF inCorrd) {
            final DisplayMetrics dm = new DisplayMetrics();
            ((Activity) mContext).getWindowManager().getDefaultDisplay().getMetrics(dm);
            int contentTop = ((Activity) mContext).getWindow().findViewById(Window.ID_ANDROID_CONTENT).getTop();
            float swidth = dm.widthPixels - contentTop;
            float sheight = dm.heightPixels;
            if (swidth * 3 > sheight * 4)
                swidth = sheight * 4 / 3;
            else
                sheight = swidth * 3 / 4;
            float Multiplier = sheight / Constants.MAX_Y * 0.85f;

            float posx = dm.widthPixels / 2.0f - Constants.MAX_X * Multiplier / 2.0f;
            float posy = (dm.heightPixels - contentTop) / 2.0f - Constants.MAX_Y * Multiplier / 2.0f;

            PointF outCorrd = new PointF();
            outCorrd.x = inCorrd.x * Multiplier;
            outCorrd.x += posx;
            outCorrd.y = inCorrd.y * Multiplier;
            outCorrd.y += posy;
            return outCorrd;
        }
    }
}
